﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Mongaba.Core.DataContracts;
using System.IO;
using Mongaba.Core.ServiceContracts;
using Mongaba.Core.Factory;
using System.Threading;
using System.ServiceModel;
using Mongaba.Core;

namespace Mongaba
{
    internal enum FileTransferMode
    { 
        Receive,
        Send
    }
    internal partial class frmFileTransfer : Form
    {
        FileTransferMode _mode;       
        ProcessContract _remoteContract;
        ProcessContract _localContract;
        String _safeFileName;
        String _operationGuid;
        String _filePath;
        long _fileLength = 0;
        long _bytesReceived = 0;
        int _bytesRead = 0;

        FileStream _flStream;

        internal frmFileTransfer(FileTransferMode mode, ProcessContract pRemoteContract)
        {
            InitializeComponent();

            Icon = Properties.Resources.Mongaba;
            _mode = mode;
            _remoteContract = pRemoteContract;
            _localContract = frmMainWindow.LocalProcess.getContract();
            if (mode == FileTransferMode.Receive)
            {
                this.Text = String.Format("Recebendo arquivo de {0}.", pRemoteContract.User.Name);               
            }
            else
            {
                this.Text = String.Format("Enviando arquivo para {0}.", pRemoteContract.User.Name);
            }
            lblDescription.Text = "Aguardando...";
        }

        internal void SendFile(string filePath,string safeFileName)
        {
            _filePath = filePath;
            _safeFileName = safeFileName;           
            this.Show();
            lblDescription.Text = "Aguardando a aceitação do usuário...";
            lblFileName.Text = String.Format("Nome do arquivo: {0}", safeFileName);
            lblFileLength.Text = string.Empty;
            bgwSendFile.RunWorkerAsync();
          
        }

     

        internal bool ReceiveFile(string fileName,string operationGuid, long fileLength)
        {
            _safeFileName = fileName;
            _fileLength = fileLength;
            _operationGuid = operationGuid;
            lblFileName.Text = String.Format("Nome do arquivo: {0}", fileName);
            lblFileLength.Text = String.Format("Tamanho: {0} bytes", fileLength);

            string message = string.Format("O usuário {0} deseja te enviar o arquivo {1}, com {2} bytes. Confirma o recebimento do arquivo?", _remoteContract.User.Name, _safeFileName, fileLength);

            if (MessageBox.Show(message, "Recebimento de arquivo", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                
                DialogResult d = fbdReceive.ShowDialog();
                if (d == DialogResult.Cancel)
                {
                    return false;
                }
                else
                {
                    this.Show();
                    _filePath = fbdReceive.SelectedPath + "\\" + _safeFileName;                   

                    _flStream = new FileStream(_filePath, FileMode.Create, FileAccess.Write);

                    frmMainWindow.LocalProcess.OnReceiveFilePart += new ReceiveFilePartRequestHandle(LocalProcess_OnReceiveFilePart);
                    frmMainWindow.LocalProcess.OnCancelReceiveFile += new CancelReceiveFileHandle(LocalProcess_OnCancelReceiveFile);
                    frmMainWindow.LocalProcess.OnEndReceiveFile += new EndReceiveFileHandle(LocalProcess_OnEndReceiveFile);
                }
                return true;
            }

            return false;


        }

        void LocalProcess_OnEndReceiveFile(string operationGuid, ProcessContract p)
        {
            if ((p.Id == _remoteContract.Id) && (_operationGuid == operationGuid))
            {
                _flStream.Close();
                finished = true;
                this.Close();
            }
        }

        void LocalProcess_OnCancelReceiveFile(string operationGuid, ProcessContract p)
        {
            if ((p.Id == _remoteContract.Id) && (_operationGuid == operationGuid))
            {
                MessageBox.Show("Cancelado pelo usuário de origem");
                finished = true;
                _flStream.Close();
                File.Delete(_filePath);
                this.Close();
            }
        }

        bool LocalProcess_OnReceiveFilePart(string operationGuid, byte[] data, int offset, int count, ProcessContract pContract)
        {
            if ((pContract.Id == _remoteContract.Id) && (_operationGuid == operationGuid))
            {
                try
                {
                    if (!stopOperation)
                    {
                        _bytesReceived += count;
                        _flStream.Write(data, offset, count);
                        int progress = Convert.ToInt32(_bytesReceived  * 100/ _fileLength);
                        pgbTransfer.Value = progress;
                        lblDescription.Text = string.Format("Recebendo o arquivo... {0}%",progress);
                        return true;
                    }
                }
                catch
                {
                    return false;
                }
                
               
            }

            return false;
        }

        


        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void frmFileTransfer_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (e.CloseReason == CloseReason.UserClosing)
            {
                if (!finished)
                {
                    if (_mode == FileTransferMode.Receive)
                    {
                        e.Cancel = !cancelReceive();
                    }
                    else
                    {
                        e.Cancel = !cancelSend();
                    }
                }
            }
            
        }
        bool stopOperation = false;
        bool finished = false;

        bool cancelReceive()
        {
          
             string message = string.Format("Deseja cancelar o recebimento do arquivo?");

             if (MessageBox.Show(message, "Recebimento de arquivo", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
             {
                 stopOperation = true;
                 _flStream.Close();
                 File.Delete(_filePath);
                
                 return true;
             }
             else
             {
                 return false;
             }
        }

        bool cancelSend()
        {

            string message = string.Format("Deseja cancelar o envio do arquivo?");

            if (MessageBox.Show(message, "Envio de arquivo", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                bgwSendFile.CancelAsync();

                try
                {
                    ProcessFactory.GetRemoteInstance(_remoteContract.Address).CancelReceiveFile(_safeFileName, _localContract);
                   
                }
                finally
                {
                   

                }
                return true;
            }
            else
            {
                return false;
            }
        }

        private void bgSendFile_DoWork(object sender, DoWorkEventArgs e)
        {
            NetTcpBinding ntcp = new NetTcpBinding();
            ntcp.Name = _remoteContract.Address;
            ntcp.ReaderQuotas.MaxArrayLength = 128000;
            ntcp.ReaderQuotas.MaxBytesPerRead = 128000;

            ntcp.TransferMode = TransferMode.Buffered;
            ntcp.Security.Mode = SecurityMode.None;
            ntcp.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
            ntcp.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.None;

            ChannelFactory<IMongabaProcess> factory = new ChannelFactory<IMongabaProcess>(ntcp, Utilities.generateProcessEndPointAddress(_remoteContract.Address));
            IMongabaProcess receiver = factory.CreateChannel();
            FileInfo fi = new FileInfo(_filePath);
            _fileLength = fi.Length;

            try
            {
                _flStream = fi.OpenRead();              
            }
            catch (IOException)
            {
                e.Result = "O arquivo está sendo utilizado por outro processo";                 
            }
            string guid = new Guid().ToString();
            if (receiver.BeginReceiveFile(_safeFileName, guid, _fileLength, frmMainWindow.LocalProcess.getContract()))
            {
               

                byte[] buffer = new byte[65000];
                float byteCount = 0;
                while ((_bytesRead = _flStream.Read(buffer, 0, 65000)) > 0)
                {
                    Thread.Sleep(100);
                    byteCount += _bytesRead;
                    int progress = Convert.ToInt32(byteCount * 100 / _fileLength);
                    bgwSendFile.ReportProgress(progress);

                    if(bgwSendFile.CancellationPending)
                    {
                        e.Result = "Recebimento cancelado pelo usuário de origem.";
                        return;
                    }

                    if (!receiver.ReceiveFilePart(guid, buffer, 0, _bytesRead, _localContract))
                    {
                        e.Result = "Recebimento cancelado pelo usuário de destino.";
                        return;
                    }
                }
                receiver.EndReceiveFile(guid, _localContract);
                e.Result = "Arquivo enviado com sucesso.";
            }
            else
            {
                e.Result = "O usuário recusou o arquivo.";
            }

          
        }

        private void bgSendFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Result != null)
            {
                MessageBox.Show(e.Result.ToString());
            }
            finished = true;
            this.Close();
            
        }

        private void bgSendFile_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pgbTransfer.Value = e.ProgressPercentage;
            lblDescription.Text = String.Format("Enviando o arquivo. {0}%",e.ProgressPercentage);
          
        }

      


    }
}
